<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JaCoCo测试覆盖率报告管理</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background-color: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            padding: 30px;
            margin-bottom: 20px;
        }
        h1 {
            color: #2c3e50;
            margin-top: 0;
            border-bottom: 2px solid #3498db;
            padding-bottom: 10px;
        }
        h2 {
            color: #2980b9;
            margin-top: 25px;
        }
        .btn {
            display: inline-block;
            background-color: #3498db;
            color: white;
            padding: 10px 20px;
            margin: 10px 5px 10px 0;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
        }
        .btn:hover {
            background-color: #2980b9;
        }
        .btn-secondary {
            background-color: #95a5a6;
        }
        .btn-secondary:hover {
            background-color: #7f8c8d;
        }
        .btn-success {
            background-color: #2ecc71;
        }
        .btn-success:hover {
            background-color: #27ae60;
        }
        .btn-warning {
            background-color: #f39c12;
        }
        .btn-warning:hover {
            background-color: #d35400;
        }
        .status-box {
            background-color: #f8f9fa;
            border-left: 4px solid #3498db;
            padding: 15px;
            margin: 20px 0;
            border-radius: 0 4px 4px 0;
        }
        .status-item {
            margin-bottom: 10px;
        }
        .status-label {
            font-weight: bold;
            display: inline-block;
            width: 200px;
        }
        .status-value {
            display: inline-block;
        }
        .status-success {
            color: #27ae60;
        }
        .status-warning {
            color: #f39c12;
        }
        .status-error {
            color: #e74c3c;
        }
        .log-container {
            background-color: #2c3e50;
            color: #ecf0f1;
            padding: 15px;
            border-radius: 4px;
            font-family: monospace;
            white-space: pre-wrap;
            max-height: 300px;
            overflow-y: auto;
            margin-top: 20px;
        }
        .spinner {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 3px solid rgba(0, 0, 0, 0.1);
            border-radius: 50%;
            border-top-color: #3498db;
            animation: spin 1s ease-in-out infinite;
            margin-left: 10px;
            vertical-align: middle;
        }
        @keyframes spin {
            to { transform: rotate(360deg); }
        }
        .hidden {
            display: none;
        }
        .footer {
            text-align: center;
            margin-top: 30px;
            color: #7f8c8d;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>JaCoCo测试覆盖率报告管理</h1>
        
        <div class="status-box" id="status-container">
            <h2>当前状态</h2>
            <div class="status-item">
                <span class="status-label">覆盖率数据文件:</span>
                <span class="status-value" id="data-file-status">加载中...</span>
            </div>
            <div class="status-item">
                <span class="status-label">数据文件大小:</span>
                <span class="status-value" id="data-file-size">加载中...</span>
            </div>
            <div class="status-item">
                <span class="status-label">数据文件最后修改时间:</span>
                <span class="status-value" id="data-file-modified">加载中...</span>
            </div>
            <div class="status-item">
                <span class="status-label">覆盖率报告:</span>
                <span class="status-value" id="report-status">加载中...</span>
            </div>
            <div class="status-item">
                <span class="status-label">报告最后生成时间:</span>
                <span class="status-value" id="report-modified">加载中...</span>
            </div>
        </div>
        
        <h2>操作</h2>
        <button id="dump-btn" class="btn">保存当前覆盖率数据</button>
        <button id="report-btn" class="btn btn-success">生成覆盖率报告</button>
        <button id="view-report-btn" class="btn btn-warning">查看覆盖率报告</button>
        <button id="refresh-btn" class="btn btn-secondary">刷新状态</button>
        
        <div id="log-container" class="log-container hidden"></div>
    </div>
    
    <div class="container">
        <h2>测试接口</h2>
        <p>点击下面的按钮可以测试应用的主要接口，这将增加代码覆盖率：</p>
        
        <button id="test-login-btn" class="btn">测试登录接口</button>
        <button id="test-register-btn" class="btn">测试注册接口</button>
        <button id="test-hello-btn" class="btn btn-secondary">测试Hello接口</button>
        <button id="test-goodbye-btn" class="btn btn-secondary">测试Goodbye接口</button>
        
        <div id="test-log-container" class="log-container hidden"></div>
    </div>
    
    <div class="container">
        <h2>JaCoCo覆盖率报告说明</h2>
        <p>JaCoCo是一个开源的代码覆盖率工具，用于Java项目。它可以帮助您了解测试覆盖了多少代码，以及哪些代码没有被测试覆盖。</p>
        
        <h3>覆盖率指标说明</h3>
        <ul>
            <li><strong>指令覆盖率 (Instruction Coverage)</strong>: 衡量代码中已执行的Java字节码指令的百分比。</li>
            <li><strong>分支覆盖率 (Branch Coverage)</strong>: 衡量代码中已执行的分支的百分比，如if语句和switch语句。</li>
            <li><strong>行覆盖率 (Line Coverage)</strong>: 衡量代码中至少执行了一次的行的百分比。</li>
            <li><strong>方法覆盖率 (Method Coverage)</strong>: 衡量代码中至少执行了一次的方法的百分比。</li>
            <li><strong>类覆盖率 (Class Coverage)</strong>: 衡量代码中至少执行了一次的类的百分比。</li>
        </ul>
        
        <h3>如何提高覆盖率</h3>
        <ol>
            <li>编写更多的单元测试和集成测试</li>
            <li>使用本页面的测试接口按钮来测试应用的主要功能</li>
            <li>确保测试覆盖所有的条件分支和异常处理路径</li>
            <li>关注覆盖率报告中标红的未覆盖代码区域</li>
        </ol>
    </div>
    
    <div class="footer">
        <p>© 2023 JaCoCo测试覆盖率报告管理系统 | 版本 1.0.0</p>
    </div>

    <script>
        // DOM元素
        const dataFileStatusEl = document.getElementById('data-file-status');
        const dataFileSizeEl = document.getElementById('data-file-size');
        const dataFileModifiedEl = document.getElementById('data-file-modified');
        const reportStatusEl = document.getElementById('report-status');
        const reportModifiedEl = document.getElementById('report-modified');
        const logContainerEl = document.getElementById('log-container');
        const testLogContainerEl = document.getElementById('test-log-container');
        
        // 按钮
        const dumpBtn = document.getElementById('dump-btn');
        const reportBtn = document.getElementById('report-btn');
        const viewReportBtn = document.getElementById('view-report-btn');
        const refreshBtn = document.getElementById('refresh-btn');
        const testLoginBtn = document.getElementById('test-login-btn');
        const testRegisterBtn = document.getElementById('test-register-btn');
        const testHelloBtn = document.getElementById('test-hello-btn');
        const testGoodbyeBtn = document.getElementById('test-goodbye-btn');
        
        // 格式化日期
        function formatDate(timestamp) {
            if (!timestamp) return '无';
            const date = new Date(timestamp);
            return date.toLocaleString();
        }
        
        // 格式化文件大小
        function formatFileSize(bytes) {
            if (bytes === 0) return '0 Bytes';
            const k = 1024;
            const sizes = ['Bytes', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }
        
        // 显示日志
        function showLog(container, message, isError = false) {
            container.classList.remove('hidden');
            const timestamp = new Date().toLocaleTimeString();
            const logClass = isError ? 'status-error' : '';
            container.innerHTML += `<div class="${logClass}">[${timestamp}] ${message}</div>`;
            container.scrollTop = container.scrollHeight;
        }
        
        // 清除日志
        function clearLog(container) {
            container.innerHTML = '';
        }
        
        // 获取JaCoCo状态
        async function getJacocoStatus() {
            try {
                const response = await fetch('/api/jacoco/status');
                const data = await response.json();
                
                // 更新UI
                dataFileStatusEl.textContent = data.dataFileExists ? '存在' : '不存在';
                dataFileStatusEl.className = 'status-value ' + (data.dataFileExists ? 'status-success' : 'status-error');
                
                dataFileSizeEl.textContent = formatFileSize(data.dataFileSize);
                dataFileModifiedEl.textContent = formatDate(data.dataFileLastModified);
                
                reportStatusEl.textContent = data.reportExists ? '已生成' : '未生成';
                reportStatusEl.className = 'status-value ' + (data.reportExists ? 'status-success' : 'status-error');
                
                reportModifiedEl.textContent = formatDate(data.reportLastModified);
                
                // 更新查看报告按钮状态
                viewReportBtn.disabled = !data.reportExists;
                
                return data;
            } catch (error) {
                showLog(logContainerEl, `获取状态失败: ${error.message}`, true);
                return null;
            }
        }
        
        // 保存覆盖率数据
        async function dumpJacocoData() {
            dumpBtn.disabled = true;
            const spinner = document.createElement('span');
            spinner.className = 'spinner';
            dumpBtn.appendChild(spinner);
            
            clearLog(logContainerEl);
            logContainerEl.classList.remove('hidden');
            showLog(logContainerEl, '正在保存覆盖率数据...');
            
            try {
                const response = await fetch('/api/jacoco/dump');
                const data = await response.json();
                
                if (data.success) {
                    showLog(logContainerEl, '覆盖率数据保存成功!');
                } else {
                    showLog(logContainerEl, `覆盖率数据保存失败: ${data.message}`, true);
                }
                
                // 刷新状态
                await getJacocoStatus();
                
            } catch (error) {
                showLog(logContainerEl, `保存覆盖率数据时出错: ${error.message}`, true);
            } finally {
                dumpBtn.removeChild(spinner);
                dumpBtn.disabled = false;
            }
        }
        
        // 生成覆盖率报告
        async function generateJacocoReport() {
            reportBtn.disabled = true;
            const spinner = document.createElement('span');
            spinner.className = 'spinner';
            reportBtn.appendChild(spinner);
            
            clearLog(logContainerEl);
            logContainerEl.classList.remove('hidden');
            showLog(logContainerEl, '正在生成覆盖率报告...');
            showLog(logContainerEl, '这可能需要一些时间，请耐心等待...');
            
            try {
                const response = await fetch('/api/jacoco/report');
                const data = await response.json();
                
                if (data.success) {
                    showLog(logContainerEl, '覆盖率报告生成成功!');
                    showLog(logContainerEl, `报告路径: ${data.reportPath}`);
                } else {
                    showLog(logContainerEl, `覆盖率报告生成失败: ${data.message}`, true);
                    if (data.output) {
                        showLog(logContainerEl, '输出:');
                        showLog(logContainerEl, data.output);
                    }
                }
                
                // 刷新状态
                await getJacocoStatus();
                
            } catch (error) {
                showLog(logContainerEl, `生成覆盖率报告时出错: ${error.message}`, true);
            } finally {
                reportBtn.removeChild(spinner);
                reportBtn.disabled = false;
            }
        }
        
        // 测试登录接口
        async function testLoginApi() {
            testLoginBtn.disabled = true;
            const spinner = document.createElement('span');
            spinner.className = 'spinner';
            testLoginBtn.appendChild(spinner);
            
            clearLog(testLogContainerEl);
            testLogContainerEl.classList.remove('hidden');
            showLog(testLogContainerEl, '正在测试登录接口...');
            
            try {
                const response = await fetch('/api/users/login', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        username: 'admin',
                        password: 'admin123'
                    })
                });
                
                const data = await response.json();
                showLog(testLogContainerEl, `登录接口响应: ${JSON.stringify(data)}`);
                
            } catch (error) {
                showLog(testLogContainerEl, `测试登录接口时出错: ${error.message}`, true);
            } finally {
                testLoginBtn.removeChild(spinner);
                testLoginBtn.disabled = false;
            }
        }
        
        // 测试注册接口
        async function testRegisterApi() {
            testRegisterBtn.disabled = true;
            const spinner = document.createElement('span');
            spinner.className = 'spinner';
            testRegisterBtn.appendChild(spinner);
            
            clearLog(testLogContainerEl);
            testLogContainerEl.classList.remove('hidden');
            showLog(testLogContainerEl, '正在测试注册接口...');
            
            // 生成随机用户名
            const randomUsername = 'testuser_' + Math.floor(Math.random() * 10000);
            
            try {
                const response = await fetch('/api/users/register', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        username: randomUsername,
                        password: 'password123'
                    })
                });
                
                const data = await response.json();
                showLog(testLogContainerEl, `注册接口响应: ${JSON.stringify(data)}`);
                
            } catch (error) {
                showLog(testLogContainerEl, `测试注册接口时出错: ${error.message}`, true);
            } finally {
                testRegisterBtn.removeChild(spinner);
                testRegisterBtn.disabled = false;
            }
        }
        
        // 测试Hello接口
        async function testHelloApi() {
            testHelloBtn.disabled = true;
            const spinner = document.createElement('span');
            spinner.className = 'spinner';
            testHelloBtn.appendChild(spinner);
            
            clearLog(testLogContainerEl);
            testLogContainerEl.classList.remove('hidden');
            showLog(testLogContainerEl, '正在测试Hello接口...');
            
            try {
                const response = await fetch('/hello');
                const data = await response.text();
                showLog(testLogContainerEl, `Hello接口响应: ${data}`);
                
            } catch (error) {
                showLog(testLogContainerEl, `测试Hello接口时出错: ${error.message}`, true);
            } finally {
                testHelloBtn.removeChild(spinner);
                testHelloBtn.disabled = false;
            }
        }
        
        // 测试Goodbye接口
        async function testGoodbyeApi() {
            testGoodbyeBtn.disabled = true;
            const spinner = document.createElement('span');
            spinner.className = 'spinner';
            testGoodbyeBtn.appendChild(spinner);
            
            clearLog(testLogContainerEl);
            testLogContainerEl.classList.remove('hidden');
            showLog(testLogContainerEl, '正在测试Goodbye接口...');
            
            try {
                const response = await fetch('/goodbye');
                const data = await response.text();
                showLog(testLogContainerEl, `Goodbye接口响应: ${data}`);
                
            } catch (error) {
                showLog(testLogContainerEl, `测试Goodbye接口时出错: ${error.message}`, true);
            } finally {
                testGoodbyeBtn.removeChild(spinner);
                testGoodbyeBtn.disabled = false;
            }
        }
        
        // 查看覆盖率报告
        function viewJacocoReport() {
            window.open('/site/jacoco/index.html', '_blank');
        }
        
        // 事件监听
        dumpBtn.addEventListener('click', dumpJacocoData);
        reportBtn.addEventListener('click', generateJacocoReport);
        viewReportBtn.addEventListener('click', viewJacocoReport);
        refreshBtn.addEventListener('click', getJacocoStatus);
        testLoginBtn.addEventListener('click', testLoginApi);
        testRegisterBtn.addEventListener('click', testRegisterApi);
        testHelloBtn.addEventListener('click', testHelloApi);
        testGoodbyeBtn.addEventListener('click', testGoodbyeApi);
        
        // 初始化
        document.addEventListener('DOMContentLoaded', getJacocoStatus);
    </script>
</body>
</html>